﻿#light

namespace Twitter.Model

open System
open System.Xml
open System.Globalization

module TwitterXMLParser =
    
    let private ExtractInt(node:XmlNode, name:string) = // empty value represented by -1
        if node.[name].InnerXml = "" then -1L
        else Int64.Parse(node.[name].InnerXml)
        
    let private ExtractString(node:XmlNode, name:string) =
        node.[name].InnerXml
    
    let private ExtractBool(node:XmlNode, name:string) =
        match node.[name].InnerXml with
        | "false" -> false
        | "true" -> true
        | _ -> raise(new ArgumentException())
        
    let private ExtractRootNode(xml:string, name:string) =
        let document = XmlDocument()
        document.LoadXml(xml)
        document.[name]
                
    let private ExtractDateTime(node:XmlNode, name:string) =
        let strings = node.[name].InnerXml.Split([|' '|]) // ddd MMM dd hh:mm:ss zone yyyy
        DateTime.Parse(String.Join(" ", [|strings.[0]; ","; strings.[2]; strings.[1]; strings.[5]; strings.[3]|]))    
        
    let ParseBoolean(boolean:string, enclosing:string) =
        let document = XmlDocument()
        document.LoadXml(boolean)
        ExtractBool(document, enclosing)

    let ParseUser(user:string) : User =
        let root = ExtractRootNode(user, "user")
        
        { 
            Id = ExtractInt(root, "id"); 
            Name = ExtractString(root, "name");
            ScreenName = ExtractString(root, "screen_name");
            Location = ExtractString(root, "location");
            Description = ExtractString(root, "description");
            ProfileImageURL = ExtractString(root, "profile_image_url");
            URL = ExtractString(root, "url"); 
            ProtectedProfile = ExtractBool(root, "protected");
            FollowersCount = ExtractInt(root, "followers_count");
            FriendsCount = ExtractInt(root, "friends_count") 
        }
        
    let private ExtractUser(node:XmlNode, name:string) = 
        ParseUser(node.[name].OuterXml)
        
    let ParseDirectMessage(message:string) : DirectMessage =
        let root = ExtractRootNode(message, "direct_message")
        
        {
            Id = ExtractInt(root, "id");
            SenderId = ExtractInt(root, "sender_id");
            RecipientId = ExtractInt(root, "recipient_id");
            Text = ExtractString(root, "text");
            CreatedAt = ExtractDateTime(root, "created_at");
            SenderScreenName = ExtractString(root, "sender_screen_name");
            RecipientScreenName = ExtractString(root, "recipient_screen_name");
        }
            
    let ParseStatus(status:string) : Status =
        let root = ExtractRootNode(status, "status")
        
        {
            Id = ExtractInt(root, "id");
            CreatedAt = ExtractDateTime(root, "created_at");
            Text = ExtractString(root, "text");
            Source = ExtractString(root, "source");
            Truncated = ExtractBool(root, "truncated");
            Favorited = ExtractBool(root, "favorited");
            InReplyToStatus = ExtractInt(root, "in_reply_to_status_id");
            InReplyToUser = ExtractString(root, "in_reply_to_screen_name");
            Owner = ExtractUser(root, "user");
        }
        
    let ParseDirectMessageList(messages:string) : list<DirectMessage> =
        let root = ExtractRootNode(messages, "direct-messages")
        
        seq { for message in root.SelectNodes("direct_message") -> ParseDirectMessage(message.OuterXml) } |> Seq.to_list
        
    let ParseUserList(users:string) : list<User> =
        let root = ExtractRootNode(users, "users")
        
        seq { for user in root.SelectNodes("user") -> ParseUser(user.OuterXml) } |> Seq.to_list
        
    let ParseStatusList(statuses:string) : list<Status> =
        let root = ExtractRootNode(statuses, "statuses")
        
        seq { for status in root.SelectNodes("status") -> ParseStatus(status.OuterXml) } |> Seq.to_list